Flutter 渲染流程
整体流程与渲染树
runApp 首帧绘制
分析的入口在 Flutter main 函数调用 runApp 方法。传入根组件,具体实现:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
其中:
- scheduleAttachRootWidget:向 EventQueue 调度一个事件,将根组件挂在到组件树上
- scheduleWarmUpFrame:快速触发一帧调度,实现快速上屏
- handleBeginFrame、handleDrawFrame 上屏流程
- scheduleFrame 开启帧调度
开启帧调度
上一步调用 binding.dart scheduleFrame 出发帧调度开启,具体流程是:
- window.scheduleFrame
- platformDispatcher.scheduleFrame
- "PlatformConfiguration_scheduleFrame"
- RuntimeController::ScheduleFrame
- Engine::ScheduleFrame
- Animator::RequestFrame
- self->AwaitVSync
等待系统下一帧 VSYNC 信号到来。
VSYNC 帧调度
当下一帧 VSYNC 信号到来,在 Animator::AwaitVSync 的回调中,判断 Layer 是否需要复用,如果不复用,则出发一帧绘制流程:
void Animator::AwaitVSync() {
waiter_->AsyncWaitForVsync(
[self = weak_factory_.GetWeakPtr()](
std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
if (self) {
if (self->CanReuseLastLayerTree()) {
self->DrawLastLayerTree(std::move(frame_timings_recorder));
} else {
self->BeginFrame(std::move(frame_timings_recorder));
}
}
});
delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_);
}
BeginFrame 透传
Animator::BeginFrame 的核心代码调用有:
- delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);
- self->delegate_.OnAnimatorNotifyIdle(Dart_TimelineGetMicros() + 100000);
- 其中的 delegate_ 是 shell
shell:
- OnAnimatorBeginFrame:engine_->BeginFrame
- OnAnimatorNotifyIdle:engine_->NotifyIdle
engine_->BeginFrame 后续的分发流程是:
- engine.cc: BeginFrame
- runtime_controller.cc: BeginFrame
- window.cc: BeginFrame
- PlatformConfiguration::BeginFrame
PlatformConfiguration::BeginFrame 中做了几件事情,核心方法如下:
// 调 dart _beginFrame
tonic::LogIfError(
tonic::DartInvoke(begin_frame_.Get(), {
Dart_NewInteger(microseconds),
Dart_NewInteger(frame_number),
}));
// 刷一遍微任务队列
UIDartState::Current()->FlushMicrotasksNow();
// 掉 dart _drawFrame
tonic::LogIfError(tonic::DartInvokeVoid(draw_frame_.Get()));
_beginFrame 调用
DartInvoke 调用的是入口方法,即 entry-point,来到 hooks.dart 下对应方法:
@pragma('vm:entry-point')
// ignore: unused_element
void _beginFrame(int microseconds, int frameNumber) {
PlatformDispatcher.instance._beginFrame(microseconds);
PlatformDispatcher.instance._updateFrameData(frameNumber);
}
lib/ui/window/platform_configuration.cc 的 _onBeginFrame:
// Called from the engine, via hooks.dart
void _beginFrame(int microseconds) {
_invoke1<Duration>(
onBeginFrame,
_onBeginFrameZone,
Duration(microseconds: microseconds),
);
}
来到 binding.dart 的 _handleBeginFrame:
void _handleBeginFrame(Duration rawTimeStamp) {
……
handleBeginFrame(rawTimeStamp);
}
handleBeginFrame 是核心方法,核心操作是 _invokeFrameCallback 进行回调调用。
_invokeFrameCallback 方法比较关键。
_drawFrame
lib/ui/window/platform_configuration.cc 的 _drawFrame:
@pragma('vm:entry-point')
// ignore: unused_element
void _drawFrame() {
PlatformDispatcher.instance._drawFrame();
}
调用到 lib/src/scheduler/binding.dart 的 _handleDrawFrame:
void _handleDrawFrame() {
if (_rescheduleAfterWarmUpFrame) {
_rescheduleAfterWarmUpFrame = false;
addPostFrameCallback((Duration timeStamp) {
_hasScheduledFrame = false;
scheduleFrame();
});
return;
}
handleDrawFrame();
}
其中:
- 先同步调 handleDrawFrame() 绘制当前帧
- 异步 addPostFrameCallback 注册下一帧调度 scheduleFrame()
handleDrawFrame 中遍历执行几个回调:
- persistentCallbacks
- postFrameCallbacks
RendererBinding.drawFrame
RendererBinding 的 initInstances 中向 SchedulerBinding 设置了一个 PersistentFrameCallback:
addPersistentFrameCallback(_handlePersistentFrameCallback);
这个回调在 SchedulerBinding 的 handleDrawFrame 方法中调用。会调到 drawFrame 方法。
@protected
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
这里需要注意的是,WidgetBinding mixin 了 drawFrame 方法,因此会首先运行 WidgetBinding.drawFrame 再运行上面这个。
WidgetBinding.drawFrame 是 Flutter Framework 中最核心的点之一,三棵树构建就在这里触发。主要包含以下操作:
- 对 dirty 元素进行重新 build:buildOwner!.buildScope
- 执行 RendererBinding.drawFrame:super.drawFrame(); 这会生成传给引擎的 Layer
- flushLayout:计算渲染对象的大小和位置
- CompositingBits:更新具有脏合成位的渲染对象
- flushPaint:将绘制命令记录到 Layer
- Compositing:将 CompositingBits 发送给 GPU,合成帧
- 更新语意树